package com.geline.mybatisplus.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.geline.mybatisplus.annotation.One2Many;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.cglib.beans.BeanMap;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mx
 * @date 2025/9/3
 */
public class CrudService<T> {

    private Class<T> entityClass;

    public CrudService(Class<T> entityClass){
        this.entityClass = entityClass;
    }

    public <V> boolean save(V object) {
        T entity = BeanUtil.toBean(object, entityClass);
        Db.save(entity);
        Serializable mainId = (Serializable) BeanMap.create(entity).get("id");
        //添加关联表记录
        saveOne2Many(object, mainId, false);
        return true;
    }

    public <V> boolean updateById(V object) {
        T entity = BeanUtil.toBean(object, entityClass);
        //更新主表记录
        Db.updateById(entity);
        Serializable mainId = (Serializable) BeanMap.create(entity).get("id");
        //先删除旧的关联记录，再添加新的关联表记录
        saveOne2Many(object, mainId, true);
        return Db.updateById(entity);
    }

    public <V> boolean saveBatch(Collection<V> objects) {
        for (V object : objects) {
            save(object);
        }
        return true;
    }

    public <V> boolean saveOrUpdate(V object) {
        T entity = BeanUtil.toBean(object, entityClass);
        Serializable id = (Serializable) BeanMap.create(entity).get("id");
        if(StrUtil.isBlankIfStr(id)){
            return save(object);
        }else {
            return updateById(object);
        }
    }

    public <V> boolean saveOrUpdate(Collection<V> objects) {
        for (V object : objects) {
            saveOrUpdate(object);
        }
        return true;
    }

    public T getById(Serializable id) {
        return Db.getById(id, entityClass);
    }

    public <V> V getVOById(Serializable id, Class<V> voClass) {
        T object = Db.getById(id, entityClass);
        return BeanUtil.toBean(object, voClass);
    }

    public <T> boolean removeById(Serializable id) {
        return Db.removeById(id, entityClass);
    }

    public <T> boolean removeByIds(Collection<Serializable> ids) {
        return Db.removeByIds(ids, entityClass);
    }

    public <V> List<V> list(AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        List<T> list = Db.list(queryWrapper);
        return BeanUtil.copyToList(list, voClass);
    }

    public List<Map<String, Object>> listMaps(AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.listMaps(queryWrapper);
    }

    public <V> List<V> listMaps(AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        List<Map<String, Object>> maps = Db.listMaps(queryWrapper);
        return BeanUtil.copyToList(maps, voClass);
    }

    public IPage<T> page(IPage page, AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.page(page, queryWrapper);
    }

    public <V> IPage<V> page(IPage page, AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        IPage<T> pages = Db.page(page, queryWrapper);
        List<T> beans = pages.getRecords();
        List<V> voList = BeanUtil.copyToList(beans, voClass);
        Page pageNew = new Page();
        pageNew.setCurrent(pages.getCurrent());
        pageNew.setSize(pages.getSize());
        pageNew.setTotal(pages.getTotal());
        pageNew.setPages(pages.getPages());
        pageNew.setRecords(voList);
        return pageNew;
    }

    public IPage<Map<String, Object>> pageMaps(IPage page, AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.pageMaps(page, queryWrapper);
    }

    public <V> IPage<V> pageMaps(IPage page, AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        IPage<Map<String, Object>> pages = Db.pageMaps(page, queryWrapper);
        List<Map<String, Object>> maps = pages.getRecords();
        List<V> voList = BeanUtil.copyToList(maps, voClass);
        Page pageNew = new Page();
        pageNew.setCurrent(pages.getCurrent());
        pageNew.setSize(pages.getSize());
        pageNew.setTotal(pages.getTotal());
        pageNew.setPages(pages.getPages());
        pageNew.setRecords(voList);
        return pageNew;
    }

    //添加关联表记录
    protected void saveOne2Many(Object object, Serializable mainId, boolean deleteOld){
        for(Field field : object.getClass().getDeclaredFields()) {
            One2Many annotation = field.getAnnotation(One2Many.class);
            if(annotation != null) {
                TableInfo tableInfo = TableInfoHelper.getTableInfo(annotation.table());
                Class<?> entityType = tableInfo.getEntityType();
                String foreignKey = annotation.foreignKey();
                foreignKey = StrUtil.toUnderlineCase(foreignKey);

                try {
                    field.setAccessible(true);
                    Collection<Object> list = (Collection<Object>) field.get(object);
                    if(list != null) {
                        if(deleteOld){
                            if(list.size()==0){
                                //删除关联表数据
                                QueryWrapper wrapper = new QueryWrapper(entityType);
                                wrapper.eq(foreignKey, mainId);
                                Db.remove(wrapper);
                            }else {
                                //1.先删除不存在的数据
                                List<Object> ids = list.stream().map(row -> BeanMap.create(row).get("id")).collect(Collectors.toList());
                                QueryWrapper wrapper = new QueryWrapper();
                                wrapper.eq(foreignKey, mainId);
                                wrapper.notIn("id", ids);
                                Db.remove(wrapper);
                            }
                        }

                        MetaObjectHandler handler = SpringUtil.getBean(MetaObjectHandler.class);
                        for(Object item : list) {
                            Object bean = BeanUtil.toBean(item, tableInfo.getEntityType());
                            String mainIdKey = StrUtil.toCamelCase(annotation.foreignKey());
                            BeanMap.create(bean).put(mainIdKey, mainId);
                            MetaObject metaObject = SystemMetaObject.forObject(bean);
                            handler.insertFill(metaObject);
                            BeanMap beanMap = BeanMap.create(bean);
                            beanMap.put(mainIdKey, mainId);
                            //2.添加或更新关联表记录
                            Db.saveOrUpdate(bean);
                        }
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
